﻿using System.Diagnostics;
using System.Security.Principal;
using Pixeldyne.Core.Extensions;
using System.Runtime.InteropServices;
using System;
using Pixeldyne.Core.Text;

namespace Pixeldyne.Core.SystemResources
{
	/// <summary>
	/// System tools for process management, etc.
	/// </summary>
	[CLSCompliant(false)]
	public static class SystemTools
	{
		private static uint cpus = 0;

		/// <summary>
		/// Gets the total number of logical processors present on current system.
		/// </summary>
		public static uint Processors
		{
			get
			{
				if (cpus == 0)
				{
					SYSTEM_INFO si;
					GetNativeSystemInfo(out si);

					cpus = si.numberOfProcessors;
				}

				return cpus;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SYSTEM_INFO
		{
			public ushort processorArchitecture;
			ushort reserved;
			public uint pageSize;
			public IntPtr minimumApplicationAddress;
			public IntPtr maximumApplicationAddress;
			public IntPtr activeProcessorMask;
			public uint numberOfProcessors;
			public uint processorType;
			public uint allocationGranularity;
			public ushort processorLevel;
			public ushort processorRevision;
		}

		[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool IsWow64Process([In] SafeHandle hProcess, [Out] out bool wow64Process);

		[DllImport("advapi32.dll", EntryPoint = "GetTokenInformation", SetLastError = true)]
		static extern bool GetTokenInformationNative(
			IntPtr TokenHandle, int TokenInformationClass, ref int TokenInformation, int TokenInformationLength,
			out int ReturnLength);

		public static bool IsVirtual()
		{
			IntPtr token = WindowsIdentity.GetCurrent().Token;

			bool virtualized = false;
			int len = 4;
			int info = 0;
			if (!GetTokenInformationNative(token, 24, ref info, len, out len)) // 24 = TokenVirtualizationEnabled
			{
				string s = "Win32 error " +
				Marshal.GetLastWin32Error().ToString();
				throw new Exception(s);
			}
			if (info != 0)
				virtualized = true;
			return virtualized;
		}

		/// <summary>
		/// Retrieves information about the current system.
		/// </summary>
		/// <param name="lpSystemInfo"></param>
		[DllImport("kernel32.dll", SetLastError = true)]
		static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);

		/// <summary>
		/// Retrieves information about the current system.
		/// </summary>
		/// <param name="lpSystemInfo"></param>
		[DllImport("kernel32.dll", SetLastError = true)]
		static extern void GetNativeSystemInfo(out SYSTEM_INFO lpSystemInfo);

		[DllImport("kernel32.dll")]  
		static extern IntPtr GetCurrentThread(); 

		/// <summary>
		/// Sets a thread affinity mask for the specified thread.
		/// </summary>
		/// 
		/// <param name="hThread">A handle to the thread whose affinity mask is to be set. This handle must have the THREAD_SET_INFORMATION and THREAD_QUERY_INFORMATION access rights.</param>
		/// <param name="dwThreadAffinityMask">The affinity mask for the thread. </param>
		[DllImport("kernel32.dll")]
		static extern IntPtr SetThreadAffinityMask(IntPtr hThread, IntPtr dwThreadAffinityMask);

		/// <summary>
		/// Sets a preferred processor for a thread. The system schedules threads on their preferred processors whenever possible.
		/// </summary>
		/// 
		/// <param name="hThread">A handle to the thread whose preferred processor is to be set </param>
		/// <param name="dwIdealProcessor">The number of the preferred processor for the thread. This value is zero-based. If this parameter is MAXIMUM_PROCESSORS, the function returns the current ideal processor without changing it. </param>
		[DllImport("kernel32.dll")]
		static extern uint SetThreadIdealProcessor(IntPtr hThread, uint dwIdealProcessor);

		/// <summary>
		/// Runs the process.
		/// </summary>
		/// <param name="command">The command to run.</param>
		/// <param name="arguments">The argumens.</param>
		/// <returns></returns>
		public static bool RunProcess(string command, params string[] arguments)
		{
			Process p = new Process();

			p.StartInfo.UseShellExecute = false;
			p.StartInfo.RedirectStandardOutput = true;
			p.StartInfo.RedirectStandardError = true;

			p.StartInfo.FileName = command;

			if (arguments != null && arguments.Length > 0)
			{
				foreach (var arg in arguments)
				{
					p.StartInfo.Arguments += arg + " ";
				}

				p.StartInfo.Arguments = p.StartInfo.Arguments.Trim();
			}

			p.Start();

			string output = p.StandardOutput.ReadToEnd();
			string error = p.StandardError.ReadToEnd();

			p.WaitForExit();

			return error.IsEmpty();
		}
	}
}